﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using EnvRide.Core.ObjectModel;
using System.IO;
using System.Xml;
using CodeMadeSimple.Common.Tests;
using CodeMadeSimple.PropertiesManagement;
using CodeMadeSimple.Common.Validation;
using CodeMadeSimple.Common.Tests.Validation;
using log4net.Appender;
using log4net.Core;

namespace EnvRide.Core.UnitTests
{
    [TestFixture]
    public class AttributeOverrideTests : OverrideTests
    {
        private const string FILE_NAME = "AttributeOverrideTests.xml";
        
        [Test]
        public void Process()
        {
            AttributeOverride attOver = new AttributeOverride();

            Process(attOver);
        }

        [Test]
        public void AppendAttribute()
        {
            EnvRide.Core.ObjectModel.Environment env = Helper.CreateDummyEnvironment();

            AttributeOverride attOver = new AttributeOverride();
            attOver.Environment = env;

            attOver.OverrideMode = AttributeOverrideMode.AppendAttribute;
            attOver.Xpath = "//*";
            attOver.AttributeName = "attributeToAppend";

            XmlDocument doc = new XmlDocument();
            EnvRideProcessArguments args = new EnvRideProcessArguments { Document = doc };
            Helper.AddPropertyContext(args);

            doc.Load(FILE_NAME);

            attOver.Process(args);

            AssertHasNoErrors(args);

            Assert.IsTrue(doc.DocumentElement.HasAttribute("attributeToAppend"));
            Assert.AreEqual(String.Empty, doc.DocumentElement.GetAttribute("attributeToAppend"));
            Assert.IsTrue(((XmlElement)doc.DocumentElement.ChildNodes[0]).HasAttribute("attributeToAppend"));
            Assert.AreEqual(String.Empty, ((XmlElement)doc.DocumentElement.ChildNodes[0]).GetAttribute("attributeToAppend"));

            attOver.NewValue = "attributeValue";

            doc.Load(FILE_NAME);

            attOver.Process(args);

            AssertHasNoErrors(args);

            Assert.IsTrue(doc.DocumentElement.HasAttribute("attributeToAppend"));
            Assert.AreEqual(attOver.NewValue, doc.DocumentElement.GetAttribute("attributeToAppend"));
            Assert.IsTrue(((XmlElement)doc.DocumentElement.ChildNodes[0]).HasAttribute("attributeToAppend"));
            Assert.AreEqual(attOver.NewValue, ((XmlElement)doc.DocumentElement.ChildNodes[0]).GetAttribute("attributeToAppend"));

            attOver.NewValue = "${" + Helper.PROP_NAME + "}";

            doc.Load(FILE_NAME);

            attOver.Process(args);

            AssertHasNoErrors(args);

            Assert.IsTrue(doc.DocumentElement.HasAttribute("attributeToAppend"));
            Assert.AreEqual(Helper.PROP_VALUE, doc.DocumentElement.GetAttribute("attributeToAppend"));
            Assert.IsTrue(((XmlElement)doc.DocumentElement.ChildNodes[0]).HasAttribute("attributeToAppend"));
            Assert.AreEqual(Helper.PROP_VALUE, ((XmlElement)doc.DocumentElement.ChildNodes[0]).GetAttribute("attributeToAppend"));

            attOver.If = "false";

            doc.Load(FILE_NAME);

            attOver.Process(args);

            AssertHasNoErrors(args);

            Assert.IsFalse(doc.DocumentElement.HasAttribute("attributeToAppend"));
            Assert.IsFalse(((XmlElement)doc.DocumentElement.ChildNodes[0]).HasAttribute("attributeToAppend"));
        }

        [Test]
        public void RemoveAttribute()
        {
            EnvRide.Core.ObjectModel.Environment env = Helper.CreateDummyEnvironment();

            AttributeOverride attOver = new AttributeOverride();
            attOver.Environment = env;

            attOver.Xpath = "//*";
            attOver.AttributeName = "attToRemove";
            attOver.OverrideMode = AttributeOverrideMode.RemoveAttribute;

            XmlDocument doc = new XmlDocument();
            EnvRideProcessArguments args = new EnvRideProcessArguments { Document = doc };
            Helper.AddPropertyContext(args);
            doc.Load(FILE_NAME);

            Assert.IsTrue(doc.DocumentElement.HasAttribute("attToRemove"));
            Assert.IsTrue(((XmlElement)doc.DocumentElement.ChildNodes[0]).HasAttribute("attToRemove"));

            attOver.Process(args);

            Assert.IsFalse(doc.DocumentElement.HasAttribute("attToRemove"));
            Assert.IsFalse(((XmlElement)doc.DocumentElement.ChildNodes[0]).HasAttribute("attToRemove"));

            doc.Load(FILE_NAME);

            attOver.AttributeName = "invalidAttToRemove";

            attOver.Process(args);

            AssertHasErrors(args, new[] { "The attribute \"invalidAttToRemove\" was not found at element \"/root\"!", "The attribute \"invalidAttToRemove\" was not found at element \"/root/child[0]\"!" });

            attOver.If = "false";

            attOver.Process(args);

            Assert.IsTrue(doc.DocumentElement.HasAttribute("attToRemove"));
            Assert.IsTrue(((XmlElement)doc.DocumentElement.ChildNodes[0]).HasAttribute("attToRemove"));
        }

        [Test]
        public void ReplaceValue()
        {
            EnvRide.Core.ObjectModel.Environment env = Helper.CreateDummyEnvironment();

            AttributeOverride attOver = new AttributeOverride();
            attOver.Environment = env;

            attOver.Xpath = "//*";
            attOver.AttributeName = "attToReplace";
            attOver.NewValue = "replacedValue";
            attOver.OverrideMode = AttributeOverrideMode.ReplaceValue;

            XmlDocument doc = new XmlDocument();
            EnvRideProcessArguments args = new EnvRideProcessArguments { Document = doc };
            Helper.AddPropertyContext(args);
            doc.Load(FILE_NAME);

            Assert.AreEqual("originalValue", doc.DocumentElement.GetAttribute("attToReplace"));
            Assert.AreEqual("originalValue", ((XmlElement)doc.DocumentElement.ChildNodes[0]).GetAttribute("attToReplace"));

            attOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(attOver.NewValue, doc.DocumentElement.GetAttribute("attToReplace"));
            Assert.AreEqual(attOver.NewValue, ((XmlElement)doc.DocumentElement.ChildNodes[0]).GetAttribute("attToReplace"));

            doc.Load(FILE_NAME);

            attOver.NewValue = "${" + Helper.PROP_NAME + "}";

            attOver.Process(args);

            AssertHasNoErrors(args);

            Assert.AreEqual(Helper.PROP_VALUE, doc.DocumentElement.GetAttribute("attToReplace"));
            Assert.AreEqual(Helper.PROP_VALUE, ((XmlElement)doc.DocumentElement.ChildNodes[0]).GetAttribute("attToReplace"));

            doc.Load(FILE_NAME);

            attOver.AttributeName = "invalidAttToReplace";

            attOver.Process(args);

            AssertHasErrors(args, new[] { "The attribute \"invalidAttToReplace\" was not found at element \"/root\"!", "The attribute \"invalidAttToReplace\" was not found at element \"/root/child[0]\"!" });

            attOver.If = "false";

            attOver.Process(args);

            Assert.AreEqual("originalValue", doc.DocumentElement.GetAttribute("attToReplace"));
            Assert.AreEqual("originalValue", ((XmlElement)doc.DocumentElement.ChildNodes[0]).GetAttribute("attToReplace"));
        }

        [Test]
        public void Serialization()
        {
            AttributeOverride attOver = new AttributeOverride();
            attOver.AttributeName = "AttributeName";
            attOver.If = "If";
            attOver.Name = "Name";
            attOver.NewValue = "NewValue";
            attOver.OverrideMode = AttributeOverrideMode.AppendAttribute;
            attOver.Xpath = "XPath";

            SerializeDeserializeAndCompare(attOver);

            attOver.AttributeName = "AttributeName2";
            attOver.If = "If2";
            attOver.Name = "Name2";
            attOver.NewValue = "NewValue2";
            attOver.OverrideMode = AttributeOverrideMode.RemoveAttribute;
            attOver.Xpath = "XPath2";

            SerializeDeserializeAndCompare(attOver);
        }

        private static void SerializeDeserializeAndCompare(AttributeOverride attOver)
        {
            AttributeOverride attOver2 = null;

            using (MemoryStream ms = new MemoryStream())
            {
                using (XmlWriter xmlW = XmlWriter.Create(ms))
                {
                    xmlW.WriteStartElement("AttributeOverride");

                    attOver.WriteXml(xmlW);

                    xmlW.WriteEndElement();
                }

                ms.Position = 0;

                using (XmlReader xmlR = XmlReader.Create(ms))
                {
                    xmlR.Read();

                    xmlR.Read();

                    attOver2 = new AttributeOverride();
                    attOver2.ReadXml(xmlR);
                }
            }

            AssertEquals(attOver, attOver2);
        }

        [Test]
        public void Validate()
        { 
            AttributeOverride attOver = new AttributeOverride();

            ValidationResult result = attOver.Validate();

            ValidationResultTests.AssertValidationResult(result, attOver, false, new IValidationMessage[] { new RequiredValueValidationMessage("Xpath"), new RequiredValueValidationMessage("AttributeName") });

            attOver.Xpath = "Xpath";

            result = attOver.Validate();

            ValidationResultTests.AssertValidationResult(result, attOver, false, new IValidationMessage[] { new RequiredValueValidationMessage("AttributeName") });

            attOver.AttributeName = "attName";

            result = attOver.Validate();

            ValidationResultTests.AssertValidationResult(result, attOver, false, false);
        }

        /// <summary>
        /// Asserts two AttributeOverride instances that should be equal.
        /// </summary>
        /// <param name="attOver1">AttributeOverride instance one.</param>
        /// <param name="attOver2">AttributeOverride instance two.</param>
        public static void AssertEquals(AttributeOverride attOver1, AttributeOverride attOver2)
        {
            Assert.AreEqual(attOver1.AttributeName, attOver2.AttributeName);
            Assert.AreEqual(attOver1.If, attOver2.If);
            Assert.AreEqual(attOver1.Name, attOver2.Name);
            Assert.AreEqual(attOver1.NewValue, attOver2.NewValue);
            Assert.AreEqual(attOver1.OverrideMode, attOver2.OverrideMode);
            Assert.AreEqual(attOver1.Xpath, attOver2.Xpath);
        }
    }
}
